﻿using System;
using System.Xml;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using tactics.Interfaces;
using tactics.Abilities;

namespace tactics
{
    public class AbilityLoader
    {
        private List<Ability> abilityList;

        #region properties

        public List<Ability> Abilities
        {
            get
            {
                return abilityList;
            }
        }

        #endregion

        public AbilityLoader(string xmlFile)
        {
            abilityList = new List<Ability>();

            Load(xmlFile);
        }

        public void Load(string xmlFile)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(xmlFile);

            // check if this is a proper xml file (for configuration)
            XmlNodeList name = doc.GetElementsByTagName("Abilities");
            if (name.Count == 0)
                return;

            // go through all the Ability nodes, create their instances, initialize them
            XmlNodeList abilities = doc.GetElementsByTagName("Ability");
            foreach (XmlNode abilityNode in abilities)
            {
                abilityList.Add(GetAbility(abilityNode));
            }
        }

        private Ability GetAbility(XmlNode abilityNode)
        {
            Ability ability = new Ability();

            ability.Id = int.Parse(abilityNode["Id"].InnerText);
            ability.Name = abilityNode["Name"].InnerText;
            ability.Description = abilityNode["Description"].InnerText;
            ability.Range = int.Parse(abilityNode["Range"].InnerText);

            XmlNode componentsNode = abilityNode["Components"];
            XmlNodeList components = componentsNode.SelectNodes("Component");

            // go through every node, creating the ability components and initializing them
            foreach (XmlNode componentNode in components)
            {
                AbilityComponent comp = new AbilityComponent();

                comp.bonus = int.Parse(componentNode.Attributes["Bonus"].Value);
                comp.defenseType = (DefenseType)Enum.Parse(typeof(DefenseType), componentNode.Attributes["Defense-Type"].Value);
                comp.radius = int.Parse(componentNode.Attributes["Radius"].Value);
                                
                // check for onHit components, add them
                XmlNode onHitNode = componentNode.SelectSingleNode("OnHit");
                if (onHitNode != null)
                    comp.onHit.AddRange(GetComponentInfoList(onHitNode.SelectNodes("ComponentInfo")));

                // check for onMiss components, add them
                XmlNode onMissNode = componentNode.SelectSingleNode("OnMiss");
                if (onMissNode != null)
                    comp.onMiss.AddRange(GetComponentInfoList(onMissNode.SelectNodes("ComponentInfo")));

                // finally add the component to the ability, and continue
                ability.AddComponent(comp);
            }

            return ability;
        }

        // returns a list of ability components, mainly for OnHit/OnMiss ability loading
        private IEnumerable<IAbilityComponent> GetComponentInfoList(XmlNodeList node)
        {
            foreach (XmlNode componentInfo in node)
                yield return GetComponentInfo(componentInfo);
        }

        // returns an ability component with the intention to have it added to a component
        private IAbilityComponent GetComponentInfo(XmlNode node)
        {
            string name = node.Attributes["name"].Value;
            IAbilityComponent ability = CreateAbilityInstance(name);

            // each attribute in the ComponentInfo node will be passed to the IAbilityComponent for initialization
            // each IAbilityComponent is expected to handle its own property setting
            foreach (XmlAttribute attribute in node.Attributes)
            {
                ability.SetProperty(attribute.Name, attribute.Value);
            }

            return ability;
        }

        // Creates an instance of an IAbilityComponent if it is found -- name must match the requested class name
        private IAbilityComponent CreateAbilityInstance(string name)
        {
            return Activator.CreateInstance("tactics", "tactics.Abilities." + name).Unwrap() as IAbilityComponent;
        }
    }
}